<?php
// +----------------------------------------------------------------------
// | 加密算法 ( Encrypt )
// | 基本思想：
// | 典型实例：
// |   1）MD5、HmacMD5在线加密
// |   2）SHA加密算法:SHA1加密，SHA224加密，SHA256加密，SHA384加密，SHA512加密
// |   3）HmacSHA1加密，HmacSHA224加密，HmacSHA256加密，HmacSHA384加密，HmacSHA512加密
// |   4）对称加密算法
// +----------------------------------------------------------------------

namespace helper\util;

class Encrypt
{
    /**
     * md5 32位密文
     * @param string $string
     * @param string $salt
     * @return string
     */
    public static function md5(string $string, string $salt = ''): string
    {
        return md5($string . $salt);
    }

    /**
     * sha1 40位密文
     * @param string $string
     * @param string $salt
     * @return string
     */
    public static function sha1(string $string, string $salt = ''): string
    {
        return sha1($string . $salt);
    }

    /**
     * sha256 64位密文
     * @param string $data
     * @return false|string
     */
    public static function sha256(string $data)
    {
        return hash("sha256", $data);
    }

    /**
     * sha512 128位密文
     * @param string $data
     * @return false|string
     */
    public static function sha512(string $data)
    {
        return hash("sha512", $data);
    }

    /**
     * hmacSHA256
     * @param string $data
     * @param string $key
     * @return false|string
     */
    public static function hmacSHA256(string $data, string $key)
    {
        return hash_hmac('sha256', $data, $key);
    }

    /**
     * crypt
     * @param string $string
     * @param string $salt
     * @return string|null
     */
    public static function crypt(string $string, string $salt = ''): ?string
    {
        return crypt($string, $salt);
    }

    /**
     * base64_encode 加密
     * @param string $string
     * @return string
     */
    public function base64_encode(string $string): string
    {
        return base64_encode($string);
    }

    /**
     * base64_decode 解密
     * @param string $string
     * @return string
     */
    public function base64_decode(string $string): string
    {
        // 替换造成乱码字符
        return base64_decode(str_replace([" ","-","_"],["+","+","/"], $string));
    }

    /**
     * urlencode 加密
     * @param string $string
     * @return string
     */
    public function urlencode(string $string): string
    {
        return urlencode($string);
    }

    /**
     * urldecode 解密
     * @param string $string
     * @return string
     */
    public function urldecode(string $string): string
    {
        return urldecode($string);
    }

    /**
     * password_hash
     * @param string $password
     * @param string $algo
     * @return false|string|null
     */
    public function password_hash(string $password, string $algo = PASSWORD_DEFAULT)
    {
        return password_hash($password, $algo);
    }

    /**
     * password_verify
     * @param string $password
     * @param string $hash
     * @return bool
     */
    public function password_verify(string $password, string $hash): bool
    {
        return password_verify($password, $hash);
    }

    /**
     *
     * @param string $hash
     * @return mixed
     */
    public function password_get_info(string $hash)
    {
        return password_get_info($hash);
    }


    /**
     * 加密
     * @param string $encrypted 待加密字符串
     * @param string $method 加密方式
     * @param string $key 对称密钥
     * @return string
     */
    public static function encrypt(string $encrypted, string $method = 'DES-EDE3', string $key = ''): string
    {
        // 填充方式说明：
        // OPENSSL_NO_PADDING   无填充
        // OPENSSL_ZERO_PADDING 0填充
        // OPENSSL_RAW_DATA    【会用PKCS7进行补位】 若此填充结果乱码则进行base64_encode转以下 解密时先base64_decode
        $encData = openssl_encrypt($encrypted, $method, $key, OPENSSL_RAW_DATA);
        return base64_encode($encData);
    }

    /**
     * 解密
     * @param string $decrypted 待解密字符串
     * @param string $method 加密方式
     * @param string $key 对称密钥
     * @return string|false
     */
    public static function decrypt(string $decrypted, string $method = 'DES-EDE3', string $key = ''): string
    {
        // 3DES（即Triple DES）是DES向AES过渡的加密算法
        // 京东支付 options设置 OPENSSL_NO_PADDING
        // 若解码出来是乱码则应将密钥用hex2bin方式转换成AASCII字符 hex2bin($key)
        return openssl_decrypt(base64_decode($decrypted), $method, $key, OPENSSL_RAW_DATA);
    }

    /**
     * 加密函数
     *
     * @param $txt
     * @param $key
     * @return string
     */
    static function passport_encrypt($txt, $key): string
    {
        $encrypt_key = md5(random_int(0, 32000));
        $ctr         = 0;
        $tmp         = '';
        for ($i = 0; $i < strlen($txt); $i++) {
            $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
            $tmp .= $encrypt_key[$ctr] . ($txt[$i] ^ $encrypt_key[$ctr++]);
        }
        return base64_encode(self::passport_key($tmp, $key));
    }

    /**
     * 解密函数
     *
     * @param $txt
     * @param $key
     * @return string
     */
    static function passport_decrypt($txt, $key): string
    {
        $txt = self::passport_key(base64_decode($txt), $key);
        $tmp = '';
        for ($i = 0; $i < strlen($txt); $i++) {
            $md5 = $txt[$i];
            $tmp .= $txt[++$i] ^ $md5;
        }
        return $tmp;
    }

    /**
     * passport_key
     *
     * @param $txt
     * @param $encrypt_key
     * @return string
     */
    static function passport_key($txt, $encrypt_key): string
    {
        $encrypt_key = md5($encrypt_key);
        $ctr         = 0;
        $tmp         = '';
        for ($i = 0; $i < strlen($txt); $i++) {
            $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
            $tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
        }
        return $tmp;
    }

    /**
     * 证书加密字符串
     * @param string $data 签名数据(需要先排序，然后拼接)
     * @param string $privateKeyPath 私钥证书路径
     * @param string $password 证书密码
     * @return string
     */
    function sign(string $data, string $privateKeyPath, string $password): string
    {
        $certs = array();
        openssl_pkcs12_read(file_get_contents($privateKeyPath), $certs, $password);
        if (!$certs) {
            return '';
        }
        $signature = '';
        //调用openssl内置签名方法，生成签名
        openssl_sign($data, $signature, $certs['pkey'], OPENSSL_ALGO_SHA256);// OPENSSL_ALGO_SHA1
        return base64_encode($signature);
    }

    /**
     * 证书解密字符串
     * @param string $data 签名数据
     * @param string $publicPath 公钥证书路径
     * @param string $password 证书密码
     * @param string $signature
     * @return int
     */
    function verify(string $data, string $publicPath, string $password, string $signature)
    {
        $certs = array();
        openssl_pkcs12_read(file_get_contents($publicPath), $certs, $password);
        if (!$certs) {
            return -1;
        }
        // openssl_verify验签成功返回1，失败0，错误返回-1
        return openssl_verify($data, base64_decode($signature), $certs['cert']);
    }

    /**
     * RSA公钥加密
     * $str是待加密字符串
     * $public_key_path公钥证书路径
     * $password 证书密码
     * return Sign签名
     */
    public function RSAEncode($str, $public_key_path)
    {
        $public_key = file_get_contents($public_key_path);
        $encrypted  = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            return NULL;
        }
        return $sign;
    }

    /**
     * RSA私钥加密
     * @param $str
     * @param $private_key
     * @return string|null
     */
    public function privateEncrypt($str, $private_key)
    {
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $private_key)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            return NULL;
        }
        return $sign;
    }

    /**
     * RSA公钥解密
     * @param string $data
     * @param string $publicKey
     * @return string|null
     */
    public function publicDecrypt(string $data, string $publicKey)
    {
        $result = openssl_public_decrypt($data, $decrypt, $publicKey);
        if ($result === false) {
            return NULL;
        } else {
            return $data;
        }
    }

    /**
     * SHA1加密AES算法加密数据
     * @param string $string 需要加密的字符串
     * @param string $key 密钥
     * @return string
     */
    public static function AESencrypt($string, string $key)
    {
        $key = substr(openssl_digest(openssl_digest($key, 'sha1', true), 'sha1', true), 0, 16);
        $data = openssl_encrypt($string, 'AES-128-ECB', $key, OPENSSL_RAW_DATA);
        return strtolower(bin2hex($data));
    }

    /**
     * SHA1加密AES算法解密数据
     * @param string $string 需要解密的字符串
     * @param string $key 密钥
     * @return string
     */
    public static function AESdecrypt($string, string $key)
    {
        $key = substr(openssl_digest(openssl_digest($key, 'sha1', true), 'sha1', true), 0, 16);
        return openssl_decrypt(hex2bin($string), 'AES-128-ECB', $key, OPENSSL_RAW_DATA);
    }

    /**
     * sha1WithRSA 生成签名
     * @param string $toSign
     * @param string $privateKey
     * @return string
     */
    public static function sha1WithRSASign(string $toSign, string $privateKey): string
    {
        $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($privateKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        $key = openssl_get_privatekey($privateKey, '');
        openssl_sign($toSign, $signature, $key);
        openssl_free_key($key);
        return base64_encode($signature);
    }

    /**
     * sha1WithRSA 校验签名
     * @param $data
     * @param $sign
     * @param $publicKey
     * @return bool
     */
    public static function sha1WithRSAVerify($data, $sign, $publicKey): bool
    {
        $sign = base64_decode($sign);
        $certificate = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($publicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
        $key = openssl_pkey_get_public($certificate);
        return openssl_verify($data, $sign, $key) === 1;
    }
}
